package com.example.pluginmanager.service.impl;

import cn.hutool.core.util.StrUtil;
import com.common.request.PageParam;
import com.common.result.PageVO;
import com.common.util.FieldValidateNotNull;
import com.common.util.ResponseUtil;
import com.example.pluginmanager.dao.UserDao;
import com.example.pluginmanager.entity.LoginUser;
import com.example.pluginmanager.entity.User;
import com.example.pluginmanager.service.UserService;
import com.example.pluginmanager.util.JwtTokenUtil;
import com.example.pluginmanager.util.RandomUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Value("${login.validateCodeCacheTimeOut}")
    private Long validateCodeCacheTimeOut;

    @Value("${login.prefix}")
    private String prefix;

    @Value("${loginOut.prefix}")
    private String loginOutPrefix;

    @Value("${loginOut.validateTokenTimeOut}")
    private Long validateTokenTimeOut;
    /*
     * 生成验证码
     * */
    public void generateValidateCode(HttpServletResponse response) throws IOException {
        // 创建字节数组用于存放图片信息
        byte[] numCodeImgByte = null;
        // 获得二进制输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        String s = RandomUtil.randomString();
        BufferedImage image = defaultKaptcha.createImage(s);

        //TODO redis缓存验证码
        ValueOperations<String, Object> stringObjectValueOperations = redisTemplate.opsForValue();
        Boolean aBoolean = stringObjectValueOperations.setIfAbsent(prefix+s, s, validateCodeCacheTimeOut, TimeUnit.MINUTES);
        // 将图片写入到流中
        ImageIO.write(image, "jpg", baos);

        // 通过response设定响应请求类型
        // no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用     缓存。
        response.setHeader("Cache-Control", "no-store");
        // no-cache指示请求或响应消息不能缓存
        response.setHeader("Pragma", "no-cache");
        /* expires是response的一个属性,它可以设置页面在浏览器的缓存里保存的时间 ,超过设定的时        间后就过期 。过期后再次
         * 浏览该页面就需要重新请求服务器发送页面数据，如果在规定的时间内再次访问次页面 就不需从服务器传送 直接从缓存中读取。
         * */
        response.setDateHeader("Expires", 0);
        // servlet接受request请求后接受图片形式的响应
        response.setContentType("image/jpeg");

        numCodeImgByte = baos.toByteArray();
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(numCodeImgByte);
        outputStream.close();
    }

    @Override
    public String login(LoginUser loginUser) {
        //TODO 验证用户 , 登录成功,返回token
        FieldValidateNotNull.checkRequireParam(loginUser, Arrays.asList("username", "password", "validateCode"));
        ValueOperations<String, Object> stringObjectValueOperations = redisTemplate.opsForValue();
        String validateCode =(String) stringObjectValueOperations.get(prefix + loginUser.getValidateCode());
        if (StrUtil.isNotEmpty(validateCode)){
            if (!validateCode.equals(loginUser.getValidateCode())){
                throw new RuntimeException("验证码无效");
            }
        }else{
            throw new RuntimeException("验证码无效");
        }
        User userByName = userDao.getUserByName(loginUser.getUsername());
        if (userByName != null) {
            //密码需要加密 MD5+salt
            String md5Pass = RandomUtil.getMd5String(userByName.getSalt()+loginUser.getPassword());
            if (userByName.getPassword().equals(md5Pass)) {
                String token = jwtTokenUtil.generateToken(userByName);
                return token;
            } else {
                throw new RuntimeException("用户名或密码错误");
            }
        } else {
            throw new RuntimeException("用户名或密码错误");
        }
    }

    @Override
    public User getUserByUsername(String username) {
        User userByName = userDao.getUserByName(username);
        return userByName;
    }

    /*
    * 退出登录 ,无效掉此token 将token存入redis,请求接口时查询redis
    * 无效的过期时间要大于token本身有效期
    * */
    public void loginOut(String accessToken){
        ValueOperations<String, Object> stringObjectValueOperations = redisTemplate.opsForValue();
        stringObjectValueOperations.setIfAbsent(loginOutPrefix + accessToken, "", validateTokenTimeOut, TimeUnit.MINUTES);
    }

    @Override
    public PageVO<User> listUserByPage(PageParam<User> pageParam) {
        //必填校验
        FieldValidateNotNull.checkRequireParam(pageParam, Arrays.asList("currentPage", "pageSize"));
        PageHelper.startPage(pageParam.getCurrentPage(), pageParam.getPageSize());
        pageParam.getObj().setStatus(1);

        List<User> users = userDao.listUserByPage(pageParam.getObj());
        Page page = (Page) users;
        PageVO<User> userPageVO = new PageVO<>();
        return ResponseUtil.returnType(userPageVO, page);
    }
}
